package command

import (
	"encoding/json"
	"errors"
	amqp "github.com/rabbitmq/amqp091-go"
	"github.com/sirupsen/logrus"
	"github.com/ztx/payment_project/common/broker"
	"github.com/ztx/payment_project/common/decorator"
	"github.com/ztx/payment_project/common/genproto/orderpd"
	"github.com/ztx/payment_project/order/app/query"
	domain "github.com/ztx/payment_project/order/domain/order"
	"golang.org/x/net/context"
)

//创建订单

type CreateOrder struct { //创建订单
	CustomerID string
	Items      []*orderpd.ItemWithQuantity
}

type CreateOrderResult struct { //返回创建结果
	OrderID string
}

type CreateOrderHandler decorator.CommandHandler[CreateOrder, *CreateOrderResult]

type createOrderHandler struct {
	orderRepo domain.Repository
	stockGRPC query.StockService
	channel   *amqp.Channel
}

func NewCreateOrderHandler( //注册指令函数
	orderRepo domain.Repository,
	stockGRPC query.StockService,
	channel *amqp.Channel,
	logger *logrus.Entry,
	metricClient decorator.MetricsClient,
) CreateOrderHandler {
	if orderRepo == nil {
		panic("nil orderRepo")
	}
	if stockGRPC == nil {
		panic("nil stockGRPC")
	}
	if channel == nil {
		panic("nil channel")
	}
	return decorator.ApplyCommandDecorators[CreateOrder, *CreateOrderResult](
		createOrderHandler{
			orderRepo: orderRepo,
			stockGRPC: stockGRPC,
			channel:   channel,
		},
		logger,
		metricClient,
	)
}

func (c createOrderHandler) Handle(ctx context.Context, cmd CreateOrder) (*CreateOrderResult, error) {
	validItems, err := c.validate(ctx, cmd.Items) //取出对应商品
	if err != nil {
		return nil, err
	}
	//err := c.stockGRPC.CheckIfItemsInStock(ctx, cmd.Items)
	//resp, err := c.stockGRPC.GetItems(ctx, []string{"123"})
	//logrus.Info("createOrderHandler || err from stock", resp)
	//var stockResponse []*orderpd.Item
	//for _, item := range cmd.Items {
	//	stockResponse = append(stockResponse, &orderpd.Item{
	//		ID:       item.ID,
	//		Quantity: item.Quantity,
	//	})
	//}

	o, err := c.orderRepo.Create(ctx, &domain.Order{
		CustomerID: cmd.CustomerID,
		Items:      validItems,
	})
	if err != nil {
		return nil, err
	}

	//创建mq队列
	q, err := c.channel.QueueDeclare(broker.EventOrderCreated, true, false, false, false, nil)
	if err != nil {
		return nil, err
	}

	marshalledOrder, err := json.Marshal(o) //json序列化订单信息
	if err != nil {
		return nil, err
	}

	//发送消息到队列
	err = c.channel.PublishWithContext(ctx, "", q.Name, false, false, amqp.Publishing{
		ContentType:  "application/json",
		DeliveryMode: amqp.Persistent,
		Body:         marshalledOrder, //具体消息
	})
	if err != nil {
		return nil, err
	}

	return &CreateOrderResult{OrderID: o.ID}, err
}

func (c createOrderHandler) validate(ctx context.Context, items []*orderpd.ItemWithQuantity) ([]*orderpd.Item, error) {
	if len(items) == 0 {
		return nil, errors.New("must have at least one items")
	}
	items = packItems(items)
	resp, err := c.stockGRPC.CheckIfItemsInStock(ctx, items) //查询商品库存
	if err != nil {
		return nil, err
	}
	return resp.Items, nil
	//var ids []string
	//for _, i := range items {
	//	ids = append(ids, i.ID)
	//}
	//return c.stockGRPC.GetItems(ctx, ids) //直接取出商品
}

// 打包订单信息（将同一物品的多次订单进行数量合并）
func packItems(items []*orderpd.ItemWithQuantity) []*orderpd.ItemWithQuantity {
	merged := make(map[string]int32)
	for _, item := range items {
		merged[item.ID] += item.Quantity
	}
	var res []*orderpd.ItemWithQuantity
	for id, quantity := range merged {
		res = append(res, &orderpd.ItemWithQuantity{
			ID:       id,
			Quantity: quantity,
		})
	}
	return res
}
